# FeedLibrary - Library for RSS-feeds
# Copyright (C) 2007 Lauri Taimila
# + Modifications Copyright 2008 Joshua Scotton <josh@joshuascotton.com>
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"

import os
from pysqlite2 import dbapi2 as sqlite

################################################################################
# This file contains three classes that together consists feed library 
# interface. These classes are FeedLibrary, Feed and Entry.
################################################################################

class FeedLibrary(object):
    """
    Feed library
    
    This library can be used to handle feeds in Entertainer.
    """
    
    def __init__(self, backend_connection, feed_db=os.path.expanduser('~/.entertainer/cache/feed.db')):
        """
        Initialize library
        @param backend_connection: BackendConnection object
        """
        
        #feed cache database
        self.feed_db = feed_db
        
        self.backend_connection = backend_connection
        self.empty = False
        if not os.path.exists(self.feed_db):
            self.empty = True
        
    def add_feed(self, url):
        """
        Add feed to the library.
        @param url: URL of the feed
        """
        self.set_empty(False)
        print "not functional yet"
        pass #TODO Edit conf file and emit message to the backend
        
    def remove_feed(self, url):
        """
        Remove feed from the library.
        @param url: URL of the feed
        """
        #TODO Edit conf file and emit message to the backend
        connection = sqlite.connect(self.feed_db)
        cursor = connection.cursor()
        cursor.execute("""DELETE FROM feed
                          WHERE url=:url""",
                          {"url" : url})
        result = cursor.fetchall()
        connection.commit()
        connection.close()
        if self.number_of_feeds() == 0:
            self.set_empty(True)
        
    def set_empty(self, empty):
        self.empty = empty
 
    def is_empty(self):
        """
        Is feed library empty
        @return True if library is empty, otherwise false
        """
        return self.empty
        
    def get_feeds(self, order='TITLE'):
        """
        Get all feeds from the library. Feeds are fetched from the feed cache 
        database. This means that when this method is called you will always
        get up-to-date feedlist.
        
        @param order: Order of returned feeds: 'TITLE' or 'UNREAD'
        @return List of 'Feed' objects
        """
        if self.is_empty():
            return []
        else:
            feeds = []
            connection = sqlite.connect(self.feed_db)
            cursor = connection.cursor()
            cursor.execute("SELECT url FROM feed ORDER BY title")
            for row in cursor:
                feeds.append(Feed(row[0], self.feed_db))
            connection.close()
            if order == "UNREAD":
                feeds.sort(self.compare_feeds_by_unread)
            return feeds
    
    def compare_feeds_by_unread(self, x, y):
        """
        This is a comparison function for the get_feeds method
        It can be used to sort a feed list by descending order of unread entries
        @param x Feed
        @param y Feed
        @return int (-1 if x's has more unread feeds than y, 1 for vice versa and 0 if equal)
        """
        return cmp(y.get_number_of_unread(), x.get_number_of_unread())
    
    def get_latest_entries(self, number=10):
        """
        Get the latest entries from the library.
        @param number: The maximum number of entries to be returned
        @return: List that contains tuples (Feed object, Entry object)
        """
        if self.is_empty():
            return []
        else:
            latest = []
            connection = sqlite.connect(self.feed_db)
            cursor = connection.cursor()
            cursor.execute("""SELECT DISTINCT feed.url, entry.id
                              FROM feed, entry
                              WHERE entry.feed_url = feed.url
                              ORDER BY entry.date DESC, entry.time  DESC
                              LIMIT 0,:max""",
                              { "max" : number } )
            for row in cursor:
                feed = Feed(row[0], self.feed_db)
                entry = feed.get_entry(row[1])
                latest.append((feed, entry))
            connection.close()
            return latest
      
    def number_of_feeds(self):
        """
        Get number of the feeds in this library
        @return Number of feeds (integer)
        """
        if self.is_empty():
            return 0
        else:
            connection = sqlite.connect(self.feed_db)
            cursor = connection.cursor()
            cursor.execute("SELECT COUNT(url) FROM feed")
            result = cursor.fetchall()
            connection.close()
            return result[0][0]
    
    def number_of_unread_entries(self):
        """
        Get total number of unread entries in this library.
        @return integer value
        """
        if self.is_empty():
            return 0
        else:
            connection = sqlite.connect(self.feed_db)
            cursor = connection.cursor()
            cursor.execute("""SELECT COUNT(feed_url) FROM entry WHERE isread='NO'""")
            result = cursor.fetchall()
            connection.close()
            return result[0][0]
        
    def request_feed_update(self):
        """
        Request backend to fetch and update all feeds. This function returns 
        immediately.
        """
        self.backend_connection.request_feed_update()
        
    def mark_all_as_read(self):
        """
        Set all entries in all feeds to read
        """
        for feed in self.get_feeds():
            for entry in feed.get_entries():
                entry.mark_as_read()

################################################################################

class Feed(object):
    """
    Objects from this class represents feeds. Feeds has entries, title etc.
    """
    
    def __init__(self, url, feed_db=os.path.expanduser('~/.entertainer/cache/feed.db')):
        """
        Initialize Feed object.
        @param url: URL of the feed
        """
        #Feed Cache Database
        self.feed_db = feed_db
        
        self.url = url
        self.entries = []
        connection = sqlite.connect(self.feed_db)
        cursor = connection.cursor()
        cursor.execute("""SELECT title, subtitle, description, time, date 
                          FROM feed 
                          WHERE url=:url""",
                          { "url" : self.url })
        result = cursor.fetchall()
        self.title = result[0][0]
        self.subtitle = result[0][1]
        self.description = result[0][2]
        self.time = result[0][3]
        self.date = result[0][4]
        cursor.execute("""SELECT title, description, id, time, date, isread 
                          FROM entry 
                          WHERE entry.feed_url=:url
                          ORDER BY date DESC, time DESC""",
                          { "url" : self.url })
        for row in cursor:
            if row[5] == "YES":
                isread = True
            else:
                isread = False
            self.entries.append(Entry(row[0], row[1], row[2], row[3], row[4],isread,feed_db))
        connection.close()
    
    def get_title(self):
        """
        Get title ot the feed
        @return Title as string
        """
        return self.title
        
    def get_description(self):
        """
        Get description of the feed.
        @return description as string
        """
        return self.description
        
    def get_entries(self):
        """
        Get all entries of this feed.
        @return List of Entry objects
        """
        
        return self.entries
    
    def get_entry(self, id):
        """
        Get a specific entry from this feed.
        @param id: ID of the entry
        @return: Entry object
        """
        for entry in self.entries:
            if entry.get_id() == id:
                return entry
        
    def get_number_of_unread(self):
        """
        Get number of unread entries in this feed.
        @return Integer - number of unread entries
        """
        connection = sqlite.connect(self.feed_db)
        cursor = connection.cursor()
        cursor.execute("""SELECT COUNT(feed_url) 
                          FROM   entry 
                          WHERE  isread='NO'
                          AND    feed_url=:url""",
                          { "url" : self.url })
        result = cursor.fetchall()
        connection.close()
        return int(result[0][0])
        
    def get_subtitle(self):
        """
        Get feed's subtitle. 
        @return: String
        """
        return self.subtitle
        
    def get_feed_url(self):
        """
        Get URL of the feed
        @return: String
        """
        return self.url
        
    def get_time(self):
        """
        Get time
        @return: String
        """
        return self.time
    
    def get_date(self):
        """
        Get date
        @return: String
        """
        return self.date

################################################################################

class Entry(object):
    """
    Entry is a one post of the feed. This is a simple container class for entry.
    """
    
    def __init__(self, title, description, id, time, date,read=False,feed_db=None):
        """
        Initialize Entry object.
        @param title: Title of the entry
        @param description: Description of the entry
        @param id: Entry ID
        @param time: Time of the entry (when this was submitted)
        @param date: Date of the entry (when this was submitted)
        @param read: whether the feed is read or not
        """
        self.title = title
        self.description = description
        self.id = id
        self.time = time
        self.date = date
        self.read = read
        self.feed_db = feed_db
    
    def get_title(self):
        """
        Get title ot the entry.
        @return: String
        """
        return self.title
        
    def get_description(self):
        """
        Get description of the entry
        @return: String 
        """
        return self.description

    def get_id(self):
        """
        Get entry ID
        @return: String
        """
        return self.id

    def get_time(self):
        """
        Get time.
        @return: String
        """
        return self.time
        
    def get_date(self):
        """
        Get date.
        @return: String
        """
        return self.date
    
    def is_read(self):
        """
        Find out whether entry is read
        @return: Boolean
        """
        return self.read
    
    def mark_as_read(self):
        """
        Mark entry as read
        """
        self._set_read(True)
    
    def mark_as_unread(self):
        """
        Mark entry as unread
        """
        self._set_read(False)
    
    def _set_read(self, read):
        """
        Set read
        @param read Boolean If set to True than it will set the entry to read, otherwise the entry will be set to unread
        """
        self.read = read
        if read:
            readval = "YES"
        else:
            readval = "NO"
        connection = sqlite.connect(self.feed_db)
        cursor = connection.cursor()
        cursor.execute("""UPDATE entry
                          SET isread=:val
                          WHERE id=:id""",
                          {"val" : readval, "id" : self.id})
        result = cursor.fetchall()
        connection.commit()
        connection.close()
